/**
 *                            License
 * THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS  
 * CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). 
 * THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW.  
 * ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR  
 * COPYRIGHT LAW IS PROHIBITED.
 * 
 * BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND  
 * AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE  
 * MAY BE CONSIDERED TO BE A CONTRACT,
 * THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED 
 * HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
 * 
 */
package lineage.model.gameworld;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import lineage.database.tables.MapsTable;
import lineage.model.L1Object;
import lineage.model.instance.L1PcInstance;
import lineage.model.map.L1Map;
import lineage.util.L1Point;
import lineage.util.collections.Lists;
import lineage.util.collections.Maps;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 游戏世界.
 * 
 * @version 2014年4月6日上午11:41:53
 * @author jrwz
 */
public final class L1World {
    private static final Logger LOG = LoggerFactory.getLogger(L1World.class);
    /** 所有对象. */
    private final ConcurrentHashMap<Integer, L1Object> allObjects;
    /** 所有角色. */
    private final ConcurrentHashMap<String, L1PcInstance> allPlayers;
    /** allObjects的Collection. */
    private Collection<L1Object> allValues;
    /** 所有对象(区分地图编号). */
    private final Map<Integer, ConcurrentHashMap<Integer, L1Object>> visibleObjects;

    /**
     * 静态初始化器，由JVM来保证线程安全.
     */
    private static class Holder {
        static L1World instance = new L1World();
    }

    /**
     * 取得该类的实例.
     */
    public static L1World getInstance() {
        return Holder.instance;
    }

    private L1World() {
        allObjects = Maps.newConcurrentHashMap();
        allPlayers = Maps.newConcurrentHashMap();
        visibleObjects = new HashMap<Integer, ConcurrentHashMap<Integer, L1Object>>();

        for (Integer mapid : MapsTable.getInstance().get().keySet()) {
            final ConcurrentHashMap<Integer, L1Object> map = Maps.newConcurrentHashMap();
            visibleObjects.put(mapid, map);
        }
    }

    /**
     * 将对象加入世界.
     * 
     * @param object
     *            - 要加入的对象
     */
    public void addObject(final L1Object object) {
        try {
            if (object == null) {
                throw new NullPointerException();
            }

            allObjects.put(object.getId(), object);

            if (object instanceof L1PcInstance) {
                final L1PcInstance pc = (L1PcInstance) object;
                allPlayers.put(pc.getName(), pc);
            }
        } catch (final Exception e) {
            LOG.error(e.getLocalizedMessage(), e);
        }
    }

    /**
     * 将对象移出世界.
     * 
     * @param object
     *            - 要移出的对象
     */
    public void removeObject(final L1Object object) {
        try {
            if (object == null) {
                throw new NullPointerException();
            }

            allObjects.remove(object.getId(), object);

            if (object instanceof L1PcInstance) {
                final L1PcInstance pc = (L1PcInstance) object;
                allPlayers.remove(pc.getName(), pc);
            }
        } catch (final Exception e) {
            LOG.error(e.getLocalizedMessage(), e);
        }
    }

    /**
     * 根据objId取得对象.
     * 
     * @param objId
     *            - 对象的唯一编号
     * @return 如果没找到则返回null
     */
    public L1Object getObject(final int objId) {
        return allObjects.get(objId);
    }

    /**
     * 取得全部对象.
     * 
     * @return 指定 collection 的不可修改视图
     */
    public Collection<L1Object> getAllObject() {
        try {
            final Collection<L1Object> vs = allValues;
            return (vs != null) ? vs : (allValues = Collections.unmodifiableCollection(allObjects.values()));
        } catch (final Exception e) {
            LOG.error(e.getLocalizedMessage(), e);
        }
        return null;
    }

    /**
     * 取得全部对象.
     * 
     * @return 缓存列表
     */
    public final Map<Integer, L1Object> getAllObjectList() {
        return allObjects;
    }

    /**
     * 将对象加入MAP内.
     * 
     * @param object
     *            - 要加入的对象
     */
    public void addVisibleObject(final L1Object object) {
        try {
            final ConcurrentHashMap<Integer, L1Object> map = visibleObjects.get(Integer.valueOf(object.getMapId()));
            if (map != null) {
                map.put(object.getId(), object);
            } else {
                LOG.error("游戏世界储存中心并未建立该地图编号资料档案：" + object.getMapId());
            }
        } catch (final Exception e) {
            LOG.error(e.getLocalizedMessage(), e);
        }
    }

    /**
     * 将对象移出MAP内.
     * 
     * @param object
     *            - 要移出的对象
     */
    public void removeVisibleObject(final L1Object object) {
        try {
            final ConcurrentHashMap<Integer, L1Object> map = visibleObjects.get(Integer.valueOf(object.getMapId()));
            if (map != null) {
                map.remove(object.getId());
            } else {
                LOG.error("游戏世界储存中心并未建立该地图编号资料档案：" + object.getMapId());
            }
        } catch (final Exception e) {
            LOG.error(e.getLocalizedMessage(), e);
        }
    }

    /**
     * 移动MAP内的对象.
     * 
     * @param object
     *            - 要移动的对象
     * @param newMapId
     *            - 要移至的新地图编号
     */
    public void moveVisibleObject(final L1Object object, final int newMapId) {
        try {
            int srcMapId = object.getMapId();
            if (srcMapId != newMapId) {
                // 取回原地图资料
                final ConcurrentHashMap<Integer, L1Object> mapSrc = visibleObjects.get(Integer.valueOf(srcMapId));
                if (mapSrc != null) {
                    mapSrc.remove(object.getId());
                } else {
                    LOG.error("游戏世界储存中心并未建立该地图编号资料档案：" + srcMapId);
                }

                // 取回新地图资料
                final ConcurrentHashMap<Integer, L1Object> map = visibleObjects.get(Integer.valueOf(newMapId));
                if (map != null) {
                    map.put(object.getId(), object);
                } else {
                    LOG.error("游戏世界储存中心并未建立该地图编号资料档案：" + newMapId);
                }
            }
        } catch (final Exception e) {
            LOG.error(e.getLocalizedMessage(), e);
        }
    }

    /**
     * 取得一定范围内的对象(排除自身).
     * 
     * @param object
     *            - 参照对象
     * @param radius
     *            范围：-1:屏幕范围内、0:坐标重叠、其他:相对距离
     * @return 该范围内的所有有效对象
     */
    public List<L1Object> getVisibleObjects(final L1Object object, final int radius) {
        final L1Map map = object.getMap();
        final L1Point pt = object.getLocation();
        final List<L1Object> result = Lists.newArrayList();

        // 取回原地图资料
        final ConcurrentHashMap<Integer, L1Object> mapSrc = visibleObjects.get(Integer.valueOf(map.getMapId()));
        if (mapSrc == null) {
            LOG.error("游戏世界储存中心并未建立该地图编号资料档案：" + map.getMapId());
            return result;
        }

        for (final L1Object element : mapSrc.values()) {
            if (element.equals(object)) {
                continue; // 排除自身
            }
            if (map != element.getMap()) {
                continue; // 与自身所在地图不符
            }

            switch (radius) {
            case -1: // 屏幕范围内
                if (pt.isInScreen(element.getLocation())) {
                    result.add(element);
                }
                break;
            case 0: // 与指定的坐标位置重叠
                if (pt.isSamePoint(element.getLocation())) {
                    result.add(element);
                }
                break;
            default: // 与指定坐标的直线距离
                int r = pt.getTileLineDistance(element.getLocation());
                if (r <= radius) {
                    result.add(element);
                }
                break;
            }
        }
        return result;
    }

    /**
     * 取得一定范围内的角色(排除自身).
     * 
     * @param object
     *            - 参照对象
     * @param radius
     *            范围：-1:屏幕范围内、0:坐标重叠、其他:相对距离
     * @return 该范围内的所有有效对象(如果没有则返回null)
     */
    public List<L1PcInstance> getVisiblePlayer(final L1Object object, final int radius) {
        final int map = object.getMapId();
        final L1Point pt = object.getLocation();
        final List<L1PcInstance> result = Lists.newArrayList();

        for (final L1PcInstance element : allPlayers.values()) {
            if (element.equals(object)) {
                continue; // 排除自身
            }
            if (map != element.getMapId()) {
                continue; // 与自身不在同一地图内
            }

            switch (radius) {
            case -1: // 屏幕范围内
                if (pt.isInScreen(element.getLocation())) {
                    result.add(element);
                }
                break;
            case 0: // 与指定的坐标位置重叠
                if (pt.isSamePoint(element.getLocation())) {
                    result.add(element);
                }
                break;
            default: // 与指定坐标的直线距离
                if (pt.getTileLineDistance(element.getLocation()) <= radius) {
                    result.add(element);
                }
                break;
            }
        }
        return result;
    }

    /**
     * 取得屏幕范围内的‘角色’(排除自身).
     * 
     * @param object
     *            - 参照对象
     * @return 该范围内的所有有效对象(如果没有则返回null)
     */
    public List<L1PcInstance> getVisiblePlayer(final L1Object object) {
        return getVisiblePlayer(object, -1); // 屏幕范围内
    }

    /**
     * 取得所有对象(区分地图编号).
     * 
     * @return 对象
     */
    public final Map<Integer, ConcurrentHashMap<Integer, L1Object>> getVisibleObjects() {
        return visibleObjects;
    }

    /**
     * 取得指定地图内的所有对象(区分地图编号).
     * 
     * @param mapId
     *            - 地图编号
     * @return 该地图内的所有对象
     */
    public final ConcurrentHashMap<Integer, L1Object> getVisibleObjects(final int mapId) {
        return visibleObjects.get(Integer.valueOf(mapId));
    }
}
